#pragma once

#include "DerivedNodeProp.h"
#include "JsiSkPoint.h"

#include <memory>
#include <utility>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"

#include "include/core/SkPoint.h"

#pragma clang diagnostic pop

namespace RNSkia {
static PropId PropNameX = JsiPropId::get("x");
static PropId PropNameY = JsiPropId::get("y");

class PointProp : public DerivedProp<SkPoint> {
public:
    explicit PointProp(PropId name, const std::function<void(BaseNodeProp *)> &onChange)
        : DerivedProp<SkPoint>(onChange)
    {
        _pointProp = defineProperty<NodeProp>(name);
    }

    void updateDerivedValue() override
    {
        if (_pointProp->isSet()) {
            // Check for JsiSkRect and JsiSkPoint
            setDerivedValue(processValue(_pointProp->value()));
        } else {
            setDerivedValue(nullptr);
        }
    }

    static SkPoint processValue(const JsiValue &value)
    {
        if (value.getType() == PropType::HostObject) {
            // Try reading as point
            auto ptr = std::dynamic_pointer_cast<JsiSkPoint>(value.getAsHostObject());
            if (ptr != nullptr) {
                return SkPoint::Make(ptr->getObject()->x(), ptr->getObject()->y());
            } else {
                // Try reading as rect
                auto ptr = std::dynamic_pointer_cast<JsiSkRect>(value.getAsHostObject());
                if (ptr != nullptr) {
                    return SkPoint::Make(ptr->getObject()->x(), ptr->getObject()->y());
                }
            }
        } else if (value.getType() == PropType::Object && value.hasValue(PropNameX) && value.hasValue(PropNameY)) {
            auto x = value.getValue(PropNameX);
            auto y = value.getValue(PropNameY);
            return SkPoint::Make(x.getAsNumber(), y.getAsNumber());
        }
        throw std::runtime_error("Expected point value.");
    }

private:
    NodeProp *_pointProp;
};
} // namespace RNSkia
